﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;

namespace CachingProject
{
    public class Cache
    {
        Set[] sets;
        int hit;
        int miss;
        int W; //word of the block address should go
        int B; //block in the set address should go
        int blockSize;
        int cacheSize;
        int numBlocks;
        int numSets;
        int blocksPerSet;
        int associativity;
        int currSets;

        public Cache(int cSize, int bSize, int assoc)
        {
            associativity = assoc;
            sets = new Set[assoc];
            hit = 0;
            miss = 0;
            cacheSize = cSize;
            blockSize = bSize;
            numSets = assoc;
            numBlocks = cacheSize / blockSize;
            blocksPerSet = numBlocks / assoc;
            int numB = numBlocks / associativity;
            for (int i = 0; i < associativity; i++)
            {
                sets[i] = new Set(numB, blockSize); //initialize set arrays
            }
        }

        public void DoCache(int address)
        {
            int temp;
            temp = address / blockSize; //temp = the truncated int division
            B = temp % blocksPerSet; //B (block number) = temp mod blocksPerSet
            W = address % blockSize; //W (word number) = address mod blockSize
        }
        public bool Search(int addy)
        {
            bool found = false;
            bool empty = false; //check for open block in one of the sets
            int index = 0;
            while (index < numSets && empty == false && found == false)
            { //while in the index range of sets, haven't found an empty set-block, and not found addy
                if (sets[index].blocks[B] == null) //if no block exists yet
                {
                    empty = true; //that set-block must be empty
                }
                else  //if set block is not empty
                {
                    found = sets[index].Search(addy, B, W); //search that set-block for the address
                    if(found == false) //if it's not there
                    {
                        index++; //check the next set
                    }
                }
            }
            if (found == true) 
            {
                Hit(index, B); //we have a hit
            }
            else
            {
                if (empty == false) //no empty slots to insert a block, must replace
                {
                    Miss(B); //we have a miss
                    int lru = LRU(B); //find the LRU set-block
                    sets[lru].Load(addy, B, W); //load the word into that set-block
                }
                else  //there is an empty slot to insert the block to
                {
                    Miss(B); //we have a miss
                    sets[index].Load(addy, B, W); //add block to empty set-block
                }
            }
            return found;
        }
        public void Hit(int index, int bnum)
        {
            for (int i = 0; i < sets.Length; i++) //increment accessed variable for each block number bNum in each set
            {
                if (sets[i].blocks[bnum] != null) //if set-block is not empty
                {
                    sets[i].Accessed(B);
                }
            }
            sets[index].ResetAccessed(B); //reset the hit block's accessed variable to 0
            hit++; //increment number of hits
        }
        public void Miss(int bNum)
        {
            for (int i = 0; i < sets.Length; i++) //increment accessed variable for each block number bNum in each set
            {
                if(sets[i].blocks[B] != null) //if set-block is not empty
                {
                    sets[i].Accessed(bNum);
                }
            }
            miss++; //increment hits
        }
        public int LRU(int bNum) //find the index in sets[] of the LRU set.
        {
            int lru = 0;
            int index = 0;
            int lruIndex = 0;
            while (index < numSets) //while within valid range of indexes
            {
                if (sets[index].GetAccessed(B) > lru) //if set-block's accessed is > the current lru accessed value
                {
                    lru = sets[index].GetAccessed(B); //get new lru accessed value
                    lruIndex = index; //save the index of current LRU
                }
                index++; //continue checking each set
            }
            return lruIndex; //return the index that has the set with theLRU block in it
        }
        public int GetHit //hit getter property
        {
            get
            {
                return hit;
            }
        }
        public int GetMiss //miss getter property
        {
            get
            {
                return miss;
            }
        }
//-----------------------------------------------------------------------------
    }
}